package com.yiban.demo.Access.Service.ServiceImp;

import com.yiban.demo.Access.DAO.AccessMapper;
import com.yiban.demo.Access.Entity.Access;
import com.yiban.demo.LoginUser.Entity.ROLE;
import com.yiban.demo.Access.Entity.RestfulUri;
import com.yiban.demo.Access.Service.AccessService;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Service
public class AccessServiceImp implements AccessService {
    private AccessMapper accessMapper;
    //这个属性是原子的，所以没有线程安全问题

    //role -> uri映射
    private AtomicReference<Map<String, HashSet<RestfulUri>>> accessMap;
    private List<PatternsRequestCondition> patternsRequestConditions;

    /**
     *
     * @param accessMapper access数据库访问层
     * @param requestMappingHandlerMapping SpringMVC提供的，在这里用于提取所有的URI
     */
    @Autowired
    public AccessServiceImp(AccessMapper accessMapper, RequestMappingHandlerMapping requestMappingHandlerMapping){
        this.accessMapper = accessMapper;
        List<Access> accessList = accessMapper.selectAllAccess();
        final Map<String, HashSet<RestfulUri>> map = groupUrl(accessList);
        accessMap = new AtomicReference<>(map);
        patternsRequestConditions = requestMappingHandlerMapping.getHandlerMethods().keySet().stream()
                .map(RequestMappingInfo::getPatternsCondition)
                .collect(Collectors.toList());
        //todo 上线前记得去掉，方便测试
//        check(patternsRequestConditions,accessList);

    }

    @Override
    public Map<String, HashSet<RestfulUri>> getAllAccess() {
        return accessMap.get();
    }

    /**
     * 从数据库中再获取一次
     */
    @Override
    public void refresh(){
        accessMap.set(groupUrl(accessMapper.selectAllAccess()));
    }


    @Override
    public int deleteAccesses(List<Integer> id) {
        int count;
        synchronized (AccessServiceImp.class) {
             count = accessMapper.deleteAccessBatchByIds(id);
        }
        if (count != 0) {
            refresh();
        }
        return count;
    }

    @Override
    public int addAccesses(List<Access> list) {
        final int count ;
        synchronized (AccessServiceImp.class) {
            count = accessMapper.insertBatch(list);
        }
        if (count != 0) {
            refresh();
        }
        return count;
    }

    @Override
    public boolean updateAccessById(Access access) {
        boolean isSuccess;
        synchronized (AccessServiceImp.class){
            isSuccess = accessMapper.updateAccessById(access);
        }

        if (isSuccess) {
            refresh();
        }
        return isSuccess;
    }

    @Override
    public List<String> getAllRole() {
        return accessMapper.selectAllRole();
    }

    /**
     * 如果有/access/{accessId}
     * 则/access/1 会解析为 /access/{accessId}
     * @param requestUri 此次请求的uri
     * @return 匹配到的uri
     * @exception TooManyResultsException 匹配多余一个 此处借用的是mybatis的异常
     */
    @Override
    public String patternPath(String requestUri) {
        String patternUri = null;
        for (PatternsRequestCondition condition : patternsRequestConditions) {
            final List<String> list = condition.getMatchingPatterns(requestUri);
            if (list.size() > 1) {
                throw new TooManyResultsException("找到多个匹配");
            }
            if (list.size() == 1){
                patternUri = list.get(0);
            }
        }
        return patternUri;
    }


    private Map<String,HashSet<RestfulUri>> groupUrl(List<Access> list){
        //预处理 建立role -> uri映射，但是存在问题，比如此时admin无法访问user能够访问的接口
        Map<String, HashSet<RestfulUri>> res = list.stream()
                .collect(Collectors.toMap(Access::getRole, this::accessToRestful, this::mergeList));
        //防止对应权限无数据库记录
        for (ROLE role : ROLE.values()) {
            res.putIfAbsent(role.getName(), new HashSet<>());
        }

        //类型过于复杂使用var简化
        //实际类型为 Iterator<Map.Entry<String, HashSet<RestfulUri>>>
        final var iterator = res.entrySet().iterator();
        //解决上面的问题，高权限合并低权限的uri
        while (iterator.hasNext()){
            final Map.Entry<String, HashSet<RestfulUri>> entry = iterator.next();
            for (String s : ROLE.valueOf(entry.getKey().toUpperCase()).getLess()) {
                entry.getValue().addAll(res.get(s));
            }
        }
        return res;
    }
    private HashSet<RestfulUri> accessToRestful(Access access){
        final HashSet<RestfulUri> set = new HashSet<>();
        set.add(access.getRestfulUri());
        return set;
    }
    private HashSet<RestfulUri> mergeList(HashSet<RestfulUri> s1, HashSet<RestfulUri> s2){
        s1.addAll(s2);
        return s1;
    }

    //防止未全部储存
    private void check(List<PatternsRequestCondition> patternsRequestConditions,List<Access> list){
        Set<String> set = patternsRequestConditions.stream()
                .flatMap(patternsRequestCondition -> patternsRequestCondition.getPatterns().stream())
                .collect(Collectors.toSet());
        set.remove("/error");
        set.removeAll(list);
        if (!set.isEmpty()){
            throw new RuntimeException("uri并未全部设定在数据库中");
        }
    }
}
